<?php

require_once dirname(dirname(__FILE__)) . '/calendarSyncCommon.php';

class CalendarSyncController {

    private $user_id;
    private $insightUser;
    private $syncQ;
    private $arrGoogleEvents;
    private $arrSalesforceEvents;
    private $arrSalesforceEventsToUpdateInGoogle;
    private $googleEventModel;
    private $salesforceEventModel;
    private $googleApiClient;
    private $calendar_id;
    private $bootstrapDone;
    private $lastSync;
    private $operation_start_timestamp;
    private $client;
    public function __construct(gCalendarSyncQ $syncQ) {
        $this->syncQ = $syncQ;
        $this->user_id = $syncQ->user_id;
        $this->insightUser = new InsightUser($syncQ->user_id);

        $this->calendar_id = $_SESSION['calender_id'];
        $this->client = new apiClient();
        if (isset($_SESSION['token'])) {
             $this->client->setAccessToken($_SESSION['token']);
                                        }
        $this->googleApiClient = new apiClient($this->client);
        $this->googleEventModel = new GoogleEvent($this->client);
        $this->salesforceEventModel = new SalesForceEvent($this->insightUser);
        $this->bootstrapDone = $this->syncQ->bootstrap_completed;
        $this->lastSync = $this->syncQ->last_sync;
    }

    public function SyncEvents($operation,$clientnew) { // Add  Veriable and user enum to run salesforce, Google and both 
        $redisLogger = RedisLogger::CalendarLogger($this->user_id, $this->calendar_id);


        $this->StartSync();

        $this->arrSalesforceEventsToUpdateInGoogle = array();
        $this->FetchGoogleEventsList($clientnew);
        $this->FetchSalesforceEventsList();
        switch ($operation) {
            case 0:
                $this->GoogleToSalesforceSync();
                $this->SalesforceToGoogleSync();
                break;
            case 1:
                
                $this->SalesforceToGoogleSync();
                break;
            case 2:
                $this->GoogleToSalesforceSync();
               
                break;
            default:
                $this->GoogleToSalesforceSync();
                $this->SalesforceToGoogleSync();
                break;
        }
        
        //at the end bootstrapping should be set to true;
        if ($this->syncQ->bootstrap_completed == false) {
            $this->syncQ->bootstrap_completed = true;
        }

        $this->FinishSync(true);
    }

    private function GoogleToSalesforceSync() {
        //CalendarSyncUtil::debug('arrGoogleEvents', $this->arrGoogleEvents);
        //CalendarSyncUtil::debug('arrSalesforceEvents', $this->arrSalesforceEvents);

        $arrSalesforceEventsIndexedBySubject = array();

        $mapper = new SalesforceEventView($this->insightUser);

        foreach ($this->arrSalesforceEvents as $event) {
            $activityDateTime = $event->IsAllDayEvent == true ? $event->ActivityDate : $event->ActivityDateTime;
            $subject = strtolower($event->Subject);

            if (array_key_exists($subject, $arrSalesforceEventsIndexedBySubject)) {
                $arrSalesforceEventsIndexedBySubject[$subject][$activityDateTime] = $event;
            } else {
                $arrSalesforceEventsIndexedBySubject[$subject] = array($activityDateTime => $event);
            }
        }

        $arrSalesforceUpsert = array();
        $arrSalesforceDelete = array();

        //this array only holds value for identifying google event id for upserted SF records
        $arrSalesforceGoogleEventId = array();

        $arrEmailAddresses = array();
        $arrSalesforceUpsertWhoIdMap = array();

        foreach ($this->arrGoogleEvents as $event) {
            //TODO: CHECK IF GOOGLE EVENT IS DELETED (eventstatus==cancelled)
            //IF YES FIND CORRESPONDING ENTRY ADD IT TO $arrGoogleEventsToDelete
            //ELSE CONTINUE AS BELOW

            CalendarSyncUtil::debug('start synchronizing google event', $event);
            $redisEntry = new gCalendarEntry($this->user_id, $this->calendar_id, $event->event_Id);
            $salesforceEvent = null;
            if (!Util::isNullOrEmpty($redisEntry->salesforce_event_id)) {
                CalendarSyncUtil::debug('redis entry found for google event {$event->event_Id}', $redisEntry);
                if (array_key_exists($redisEntry->salesforce_event_id, $this->arrSalesforceEvents)) {
                    if ($event->eventstatus == "cancelled") {
                        CalendarSyncUtil::debug('google event is cancelled, need to delete it from SF');
                        $arrSalesforceDelete[] = $this->arrSalesforceEvents[$redisEntry->salesforce_event_id];
                    } else {
                        CalendarSyncUtil::debug('google event is not cancelled, need to update it into SF');
                        $salesforceEvent = $this->arrSalesforceEvents[$redisEntry->salesforce_event_id];
                    }
                }
            } else if (array_key_exists(strtolower($event->summary), $arrSalesforceEventsIndexedBySubject)) {
                CalendarSyncUtil::debug('redis entry not found for google event {$event->event_Id}');
                if (array_key_exists($event->start_DateTime, $arrSalesforceEventsIndexedBySubject[strtolower($event->summary)])) {
                    $salesforceEvent = $arrSalesforceEventsIndexedBySubject[strtolower($event->summary)][$event->start_DateTime];
                    CalendarSyncUtil::debug('salesforce event found by subject and timestamp for google event {$event->event_Id}', $salesforceEvent);
                }
                else {
                    CalendarSyncUtil::debug('salesforce event not found by subject and timestamp for google event {$event->event_Id}');
                }
            }

            if ($salesforceEvent == null) { //new record insert it
                $salesforceEventForUpsert = $mapper->createModelFromGoogleEvent($event);
                CalendarSyncUtil::debug('salesforce event created by conversion for google event {$event->event_Id}', $salesforceEventForUpsert);
                $arrSalesforceUpsert[] = $salesforceEventForUpsert;
                $arrSalesforceGoogleEventId[] = $event->event_Id;
                CalendarSyncUtil::debug('WhoId for google event {$event->event_Id}', $event->WhoId);
                if ($event->WhoId != null && !Util::isNullOrEmpty($event->WhoId->email)) {
                    CalendarSyncUtil::debug('WhoId and WhoId.email not null for google event {$event->event_Id}');
                    $arrSalesforceUpsertWhoIdMap[$event->WhoId->email] = $salesforceEventForUpsert;
                    $arrEmailAddresses[] = $event->WhoId->email;
                }
            } else {
                CalendarSyncUtil::debug('google event last modified datetime', $event->updated);
                CalendarSyncUtil::debug('salesforce event last modified datetime', $salesforceEvent->LastModifiedDate);
                CalendarSyncUtil::debug('last sync datetime', $this->lastSync);
                
                if ($this->bootstrapDone == true && $event->updated <= $this->lastSync && $salesforceEvent->LastModifiedDate <= $this->lastSync) {
                    CalendarSyncUtil::debug('Salesforce event found but Bootstrapping is done but none of the salesforce or google event is updated later than last_sync. Moving on to next google event in list.');
                    continue;
                } else {
                    if ($event->updated > $salesforceEvent->LastModifiedDate) {
                        CalendarSyncUtil::debug('Google event updated later than Salesforce event. Updating Salesforce event..');
                        $salesforceEvent_updated = $mapper->createModelFromGoogleEvent($event);
                        $salesforceEvent_updated->Id = $salesforceEvent->Id;
                        $arrSalesforceUpsert[] = $salesforceEvent_updated;
                        $arrSalesforceGoogleEventId[] = $event->event_Id;
                        CalendarSyncUtil::debug('WhoId for google event {$event->event_Id}', $event->WhoId);
                        if ($event->WhoId != null && !Util::isNullOrEmpty($event->WhoId->email)) {
                            CalendarSyncUtil::debug('WhoId and WhoId.email not null for google event {$event->event_Id}');
                            $arrSalesforceUpsertWhoIdMap[$event->WhoId->email] = $salesforceEvent_updated;
                            $arrEmailAddresses[] = $event->WhoId->email;
                        }
                    } else if ($event->updated < $salesforceEvent->LastModifiedDate) {
                        //MUBASHIR: PLEASE UPDATE GOOGLE EVENTS FIELD FROM SALESFORCE RECORD
                        //AND UPDATE THAT INTO GOOGLE. FOR NOW, I AM JUST PUTTING GOOGLE UPDATES
                        //INTO AN ARRAY $arrGoogleUpsert
                        CalendarSyncUtil::debug('Salesforce event updated later than Google event. Updating Google event..');
                        $this->arrSalesforceEventsToUpdateInGoogle[] = $event;
                    }
                }
                CalendarSyncUtil::debug('Removing Salesforce event from to-be-synced list', $salesforceEvent->Id);
                unset($this->arrSalesforceEvents[$salesforceEvent->Id]);
            }
            CalendarSyncUtil::debug('Removing Google event from pending event list', $event->event_Id);
            unset($this->arrGoogleEvents[$event->event_Id]);
            CalendarSyncUtil::debug('finished synchronizing google event', $event->event_Id);
        }

        //Populating WhoId on Salesforce event records for upsert
        if (count($arrEmailAddresses) > 0) {
            $arrResult = Salesforce::lookupEmails($arrEmailAddresses);
            foreach ($arrResult as $email => $record) {
                if (array_key_exists($email, $arrSalesforceUpsertWhoIdMap)) {
                    $salesforceEvent = $arrSalesforceUpsertWhoIdMap[$email];
                    $salesforceEvent->WhoId = $record->Id;
                }
            }
        }


        CalendarSyncUtil::debug('arrSalesforceUpsert', $arrSalesforceUpsert);
        $arrResults = $this->salesforceEventModel->upsertEvents($arrSalesforceUpsert);
        CalendarSyncUtil::debug('arrResults', $arrResults);
        $redisLogger = RedisLogger::CalendarLogger($this->user_id, $this->calendar_id);

        for ($i = 0; $i < count($arrResults); $i++) {
            $result = $arrResults[$i];
            $salesforceEvent = $arrSalesforceUpsert[$i];

            $activityDateTime = $salesforceEvent->IsAllDayEvent == true ? $salesforceEvent->ActivityDate : $salesforceEvent->ActivityDateTime;
            $subject = $salesforceEvent->Subject;
            $googleEventId = $arrSalesforceGoogleEventId[$i];
            $salesforceEventId = null;
            $error = null;

            if ($result->success) {

                if ($result->created) {
                    $redisEntry = new gCalendarEntry($this->user_id, $this->calendar_id, $googleEventId);
                    $redisEntry->user_id = $this->user_id;
                    $redisEntry->salesforce_event_id = $result->id;
                    $redisEntry->insert();
                }
                $salesforceEventId = $result->id;
            } else {
                if (isset($result->id)) {
                    $salesforceEventId = $result->id;
                }
                $error = $result->errors[0];
            }

            //Logging to redis for user consumable logs
            $calendSyncLogEntry = new CalendarSyncLogEntry();
            $calendSyncLogEntry->goog_event_id = $googleEventId;
            $calendSyncLogEntry->startDateTime = $activityDateTime;
            $calendSyncLogEntry->summary = $salesforceEvent->Subject;
            $calendSyncLogEntry->sfdc_event_id = $salesforceEventId;
            $redisLogger->log($calendSyncLogEntry);

            //Logging to file for support logs
            $this->LogToFile($activityDateTime, $subject, $googleEventId, $salesforceEventId, $error, ($i == 0));
        }

        //Deleted Salesforce Events that are deleted from Google
        $arrDeleteResults = $this->salesforceEventModel->deleteEvents($arrSalesforceDelete);
        CalendarSyncUtil::debug('arrDeleteResults', $arrDeleteResults);
        //TODO: Now you need to iterate through list of Salesforce Events and Insert them in Google
        //And those that are deleted must be deleted from Google as well.
        $redisLogger->commit();
    }
    
   private function SalesforceToGoogleSync() {
        $arrGoogleInserts = array();
        $arrGoogleDeletes = array();
        $arrGoogleUpdates = array();
        $calendarId = $this->calendar_id;

        CalendarSyncUtil::debug('this->arrSalesforceEvents', $this->arrSalesforceEvents);
        CalendarSyncUtil::debug('this->arrSalesforceEventsToUpdateInGoogle', $this->arrSalesforceEventsToUpdateInGoogle);
        foreach ($this->arrSalesforceEvents as $sfEvent) {
            if ($sfEvent->getIsDeleted() == true) {
                //add to $arrGoogleDeletes
                $redisMapper = new sfCalendarEntry($this->user_id, $sfEvent->getId());
                if (!Util::isNullOrEmpty($redisMapper->google_event_id)) {
                    //$arrGoogleDeletes[] = $redisMapper->google_event_id;
                    $calenderEvent = new GoogleEvent($this->googleApiClient);
                    $calenderEvent->delete($calendarId, $redisMapper->google_event_id);
                    //Write batch delet here.
                }
            } else {
                //add to $arrGoogleInserts

                $calenderEvent = new GoogleEvent($this->googleApiClient);
                $calenderEvent = GoogleEventView::createGoogleEventFromSalesForceEvent($sfEvent, $calendarId);
                $calenderEvent->insert($calendarId, $calenderEvent); // Insert all the events that are not in sales foce here.
                //Insert Redis mapping
                $redisMapper = new gCalendarEntry($this->user_id, $this->calendar_id, $calenderEent->event_Id);
                $redisMapper->salesforce_event_id = $sfEvent->geId();
                $redisMapper->insert();
                //Logging to redis for user consumable logs
                $calendSyncLogEntry = new CalendarSyncLogEntry();
                $calendSyncLogEntry->goog_event_id = $calenderEvent->event_Id;
                $calendSyncLogEntry->startDateTime = $calenderEvent->start_DateTime;
                $calendSyncLogEntry->summary = $calenderEvent->summary;
                $calendSyncLogEntry->sfdc_event_id = $sfEvent->getId();
                $calendSyncLogEntry->Action_Type = "Insert";
                $redisLogger->log($calendSyncLogEntry);

                //Logging to file for support logs
                $this->LogToFile($calenderEvent->start_DateTime, $calenderEvent->description, $calenderEvent->event_Id, $sfEvent->getId(), $error, ($i == 0));
                CalendarSyncUtil::debug('Inserted GoogleEvent', $calenderEvent);
            }
        }

        foreach ($this->arrSalesforceEventsToUpdateInGoogle as $sfEvent) {
            //add to $arrGoogleUpdates            
            $redisMapper = new sfCalendarEntry($this->user_id, $sfEvent->getId());
            $arrGoogleInserts[] = $redisMapper->google_event_id;
            $calenderEvent = new GoogleEvent($this->googleApiClient);
            $calenderEvent->getevent($calendarId, $redisMapper->google_event_id);
            if ($sfEvent->getLastModifiedDate() > $calenderEvent->updated) {
                $calenderEvent = GoogleEventView::createGoogleEventFromSalesForceEvent($sfEvent, $calendarId);
                $calenderEvent->update($calenderId, $calenderEvent); //Update if sales force is updated after google calender.
                //Logging to redis for user consumable logs
                $calendSyncLogEntry = new CalendarSyncLogEntry();
                $calendSyncLogEntry->goog_event_id = $calenderEvent->event_Id;
                $calendSyncLogEntry->startDateTime = $calenderEvent->start_DateTime;
                $calendSyncLogEntry->summary = $calenderEvent->summary;
                $calendSyncLogEntry->sfdc_event_id = $sfEvent->getId();
                $calendSyncLogEntry->Action_Type = "Update";
                $redisLogger->log($calendSyncLogEntry);

                //Logging to file for support logs
                $this->LogToFile($calenderEvent->start_DateTime, $calenderEvent->description, $calenderEvent->event_Id, $sfEvent->getId(), $error, ($i == 0));
                CalendarSyncUtil::debug('Updated GoogleEvent', $calenderEvent);
            }
        }
        
        $redisLogger->commit();
        //TODO : Mubashar,Create a single batch object on top and queue all these calls here...
        //iterate through $arrGoogleInserts and create batch : Done without batch
        //iterate through $arrGoogleDeletes and append to batch : Done without batch
        //iterate through $arrGoogleUpdates and append to batch : Done without batch
        //
        //batch operations for google are limited to 100 operations so need to make sure : Done without batch
        //we don't exceed that limit. : Done without batch
    }

    private function FetchGoogleEventsList($clientnew) {
        $googleevent = new GoogleEvent($clientnew);
        $this->arrGoogleEvents = $googleevent->getEventList($this->calendar_id);
        //$this->arrGoogleEvents = $this->googleEventModel->getEventList($this->calendar_id);
    }

    private function FetchSalesforceEventsList() {
        $this->arrSalesforceEvents = $this->salesforceEventModel->getEvents();
    }

    private function LogToFile($event_startDateTime, $event_summary, $google_event_ID, $salesforce_event_ID, $error_info, $is_first_line = false, $is_last_line = false) {
        $dbg = "";
        if ($is_first_line == true) {
            $dbg = "\n\n" . "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" . "\n";
        }
        $dbg .= "\n" . "\"{$this->user_id}\"|\"{$this->calendar_id}\"|\"{$this->operation_start_timestamp}\"|\"{$event_startDateTime}\"|\"{$event_summary}\"|\"{$google_event_ID}\"|\"{$salesforce_event_ID}\"|\"{$error_info}\"" . "\n";
        if ($is_last_line == true) {
            $dbg.= "\n\n" . "-----------------------------------------------------------------------------------------------" . "\n";
        }

        $logfile = DEBUG_LOG_DIR . "/" . $this->user_id . "_" . $this->calendar_id . "_" . date("mdY") . ".log";
        error_log($dbg, 3, $logfile); // write to LOG_FILE
    }

    public function StartSync() {
        $this->operation_start_timestamp = CalendarSyncUtil::getCurrentGMTTime();

        $this->syncQ->sync_start = $this->operation_start_timestamp;
        $this->syncQ->in_progress = true;
        $this->syncQ->update();
    }

    public function FinishSync($success, $dt = null) {
        if ($success == true) {
            $gmdate = CalendarSyncUtil::getCurrentGMTTime($datetime = $dt);
            CalendarSyncUtil::debug('GMT date for last sync', $gmdate);
            $this->syncQ->last_sync = $gmdate;
        }
        $this->syncQ->in_progress = $success;
        $this->syncQ->update();
    }

}

?>
